package com.lalameow.researchstation.screen.widget.tree

import com.lalameow.researchstation.researching.ResearchingTechnology
import com.lalameow.researchstation.screen.widget.DynamicItemIconWidget
import com.lalameow.researchstation.util.MinecraftClient
import com.lalameow.researchstation.util.Offset
import com.lalameow.researchstation.util.drawNineGridTexture
import com.lalameow.researchstation.util.translate
import com.lalameow.researchstation.util.x
import com.lalameow.researchstation.util.y
import com.mojang.blaze3d.systems.RenderSystem
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.client.font.TextRenderer
import net.minecraft.client.gui.DrawableHelper
import net.minecraft.client.render.GameRenderer
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.client.util.math.Rect2i
import net.minecraft.util.Identifier

@Environment(EnvType.CLIENT)
internal class ResearchingNodeWidget(
    val technology: ResearchingTechnology,
    private val textRenderer: TextRenderer
) :
    DrawableHelper() {
    companion object {
        private val WIDGETS_TEXTURE = Identifier("textures/gui/advancements/widgets.png")
        const val NODE_SIZE = 26
        const val PADDING = 2
        private const val TITLE_PADDING_LEFT = PADDING + NODE_SIZE
    }

    private val iconWidget = DynamicItemIconWidget(technology.icon.toList())
    private val titleWidth = TITLE_PADDING_LEFT + textRenderer.getWidth(technology.name) + 4 * PADDING
    private val descriptions = technology.description?.let { textRenderer.wrapLines(it, titleWidth - 2 * PADDING) }
    private val descriptionHeight = (descriptions?.size ?: 0) * textRenderer.fontHeight

    fun MatrixStack.draw(
        position: Offset,
        researched: Boolean,
        screenX: Int,
        screenY: Int,
        mouse: Offset
    ) {
        RenderSystem.enableDepthTest()
        push()
        val hovering = isMouseOn(position, screenX, screenY, mouse)
        if (hovering) {
            translate(0, 0, 400)
            drawTooltip(position.x to position.y, researched)
        }
        if (technology.criteria.isNotEmpty()) {
            RenderSystem.setShaderColor(0.7f, 0.7f, 0.7f, 1.0f)
        }
        drawWidget(position, researched)
        val zOffset = MinecraftClient.itemRenderer.zOffset
        if (hovering) MinecraftClient.itemRenderer.zOffset = 400F
        drawIcon(screenX + position.x + 5 to screenY + position.y + 5)
        RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f)
        MinecraftClient.itemRenderer.zOffset = zOffset
        pop()
    }

    private fun drawIcon(position: Offset) {
        with(iconWidget) { draw(position.x, position.y) }
    }

    private fun MatrixStack.drawWidget(position: Offset, researched: Boolean) {
        RenderSystem.setShader(GameRenderer::getPositionTexShader)
        RenderSystem.setShaderTexture(0, WIDGETS_TEXTURE)
        drawTexture(
            this,
            position.x,
            position.y,
            0,
            128 + if (researched) 0 else NODE_SIZE,
            NODE_SIZE,
            NODE_SIZE
        )
    }

    private fun MatrixStack.drawTooltip(position: Offset, researched: Boolean) {
        RenderSystem.setShader(GameRenderer::getPositionTexShader)
        RenderSystem.setShaderTexture(0, WIDGETS_TEXTURE)

        if (!descriptions.isNullOrEmpty()) {
            drawNineGridTexture(
                Rect2i(
                    position.x - PADDING,
                    position.y + 19,
                    titleWidth,
                    descriptionHeight + 13 /* 1 + 4 * 2(PADDING) + 2 * 2(BORDER) + 2(MARGIN) */
                ),
                Rect2i(0, 2 * NODE_SIZE + 3, 200, 20),
                Rect2i(2, 2, 196, 16)
            )
        }

        drawNineGridTexture(
            Rect2i(position.x - PADDING, position.y + 3, titleWidth, 20),
            Rect2i(0, if (researched) 3 else NODE_SIZE + 3, 200, 20),
            Rect2i(2, 2, 196, 16)
        )

        textRenderer.drawWithShadow(
            this,
            technology.name,
            (position.x + TITLE_PADDING_LEFT).toFloat(),
            (position.y + 9).toFloat(),
            -1
        )
        var cursor = NODE_SIZE

        if (!descriptions.isNullOrEmpty()) {
            for (line in descriptions) {
                textRenderer.drawWithShadow(
                    this,
                    line,
                    (position.x + 1 + PADDING).toFloat(),
                    (position.y + cursor + 1).toFloat(),
                    -1
                )
                cursor += 9
            }
        }
    }

    private fun isMouseOn(position: Offset, screenX: Int, screenY: Int, mouse: Offset): Boolean {
        val i = screenX + position.x
        val j = screenY + position.y
        return mouse.x in i until i + NODE_SIZE && mouse.y in j until j + NODE_SIZE
    }
}
